#pragma once

#include <logstream/log_stream.h>

#include <atomic>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <exception>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <memory>
#include <mutex>
#include <optional>
#include <ostream>
#include <sstream>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>

#if __cplusplus >= 202002L
#include <source_location>
#endif

#if defined(__has_include)
#if __has_include(<stacktrace>)
#define ASSERT_HAS_STD_STACKTRACE 1
#include <stacktrace>
#else
#define ASSERT_HAS_STD_STACKTRACE 0
#endif
#else
#define ASSERT_HAS_STD_STACKTRACE 0
#endif

// Optional type-name demangle support
#if defined(__has_include)
#if __has_include(<cxxabi.h>)
#define ASSERT_HAS_CXXABI 1
#include <cxxabi.h>
#else
#define ASSERT_HAS_CXXABI 0
#endif
#else
#define ASSERT_HAS_CXXABI 0
#endif

// POSIX stacktrace headers (only when enabled; keep outside namespace)
#if defined(__has_include)
#if __has_include(<dlfcn.h>)
#define ASSERT_HAS_DLFCN 1
#include <dlfcn.h>
#else
#define ASSERT_HAS_DLFCN 0
#endif
#else
#define ASSERT_HAS_DLFCN 0
#endif

#if defined(__has_include)
#if __has_include(<execinfo.h>)
#define ASSERT_HAS_EXECINFO 1
#include <execinfo.h>
#else
#define ASSERT_HAS_EXECINFO 0
#endif
#else
#define ASSERT_HAS_EXECINFO 0
#endif

#if (ASSERT_HAS_DLFCN && ASSERT_HAS_EXECINFO)
#define ASSERT_HAS_POSIX_BACKTRACE 1
#else
#define ASSERT_HAS_POSIX_BACKTRACE 0
#endif

namespace kp
{

namespace Assertor
{
// =====================================
// format: stringify_value helpers
// =====================================

template <typename T, typename = void>
struct is_stream_insertable : std::false_type
{
};

template <typename T>
struct is_stream_insertable<
    T,
    std::void_t<decltype(std::declval<std::ostream &>() << std::declval<const T &>())>>
    : std::true_type
{
};

template <typename T>
constexpr bool StreamInsertable = is_stream_insertable<T>::value;

template <typename T, typename = void>
struct has_begin_end : std::false_type
{
};

template <typename T>
struct has_begin_end<T,
                     std::void_t<decltype(std::begin(std::declval<const T &>())),
                                 decltype(std::end(std::declval<const T &>()))>> : std::true_type
{
};

template <typename T>
constexpr bool HasBeginEnd = has_begin_end<T>::value;

inline std::string
demangle_type_name(const char * name)
{
#if ASSERT_HAS_CXXABI
    int status = 0;
    char * demangled = abi::__cxa_demangle(name, nullptr, nullptr, &status);
    if (demangled && status == 0)
    {
        std::string s(demangled);
        std::free(demangled);
        return s;
    }
    if (demangled)
        std::free(demangled);
    return std::string(name);
#else
    return std::string(name);
#endif
}

template <typename A, typename B>
inline std::string
stringify_value(std::pair<A, B> const & p)
{
    std::ostringstream os;
    os << '(';
    if constexpr (StreamInsertable<A>)
    {
        if constexpr (std::is_floating_point_v<A>)
            os << std::setprecision(std::numeric_limits<A>::max_digits10) << std::defaultfloat
               << p.first;
        else
            os << p.first;
    }
    else
        os << stringify_value(p.first);
    os << " -> ";
    if constexpr (StreamInsertable<B>)
    {
        if constexpr (std::is_floating_point_v<B>)
            os << std::setprecision(std::numeric_limits<B>::max_digits10) << std::defaultfloat
               << p.second;
        else
            os << p.second;
    }
    else
        os << stringify_value(p.second);
    os << ')';
    return os.str();
}

template <typename T>
inline std::string
stringify_value(const T & value)
{
    if constexpr (StreamInsertable<T>)
    {
        std::ostringstream os;
        if constexpr (std::is_floating_point_v<T>)
        {
            os << std::setprecision(std::numeric_limits<T>::max_digits10) << std::defaultfloat
               << value;
        }
        else
        {
            os << value;
        }
        return os.str();
    }
    else if constexpr (HasBeginEnd<T> && !std::is_same_v<T, std::string>)
    {
        std::ostringstream os;
        os << '[';
        bool first = true;
        for (const auto & elem : value)
        {
            if (!first)
                os << ", ";
            first = false;
            if constexpr (StreamInsertable<decltype(elem)>)
            {
                using Elem = std::decay_t<decltype(elem)>;
                if constexpr (std::is_floating_point_v<Elem>)
                    os << std::setprecision(std::numeric_limits<Elem>::max_digits10)
                       << std::defaultfloat << elem;
                else
                    os << elem;
            }
            else
                os << stringify_value(elem);
        }
        os << ']';
        return os.str();
    }
    else
    {
        std::ostringstream os;
        os << "<unprintable type=" << demangle_type_name(typeid(T).name()) << ">";
        return os.str();
    }
}

// =====================================
// policy: SourceLocation and failure policy
// =====================================
#if __cplusplus >= 202002L
/**
 * @brief 源位置封装。
 * @details 在 C++20 及以上，别名为 `std::source_location`；在较低版本提供兼容结构体。
 * - 使用 `ASSERT_CURRENT_SOURCE_LOCATION` 宏统一获取当前点位。
 * - 兼容实现仅保存文件名、函数名与行号；不进行动态分配。
 */
using SourceLocation = std::source_location;
#else
/**
 * @brief 源位置兼容实现（低于 C++20）。
 * @details 提供 `current()`/`file_name()`/`function_name()`/`line()` 只读接口。
 */
struct SourceLocation
{
    const char * _file;
    const char * _function;
    unsigned int _line;

    static SourceLocation current(const char * file = __FILE__,
                                  unsigned int line = __LINE__,
                                  const char * function = nullptr) noexcept
    {
        return SourceLocation{file, function ? function : "<unknown>", line};
    }

    const char * file_name() const noexcept { return _file; }
    const char * function_name() const noexcept { return _function; }
    unsigned int line() const noexcept { return _line; }
};
#endif

#if __cplusplus >= 202002L
/**
 * @brief 统一获取当前源位置的宏。
 * @details 根据语言标准选择 `SourceLocation::current()` 参数形式。
 */
#define ASSERT_CURRENT_SOURCE_LOCATION SourceLocation::current()
#else
#define ASSERT_CURRENT_SOURCE_LOCATION SourceLocation::current(__FILE__, __LINE__, __func__)
#endif

/**
 * @brief 运行时失败处理策略。
 * @details `Throw`/`Abort`/`LogOnly` 三种模式，默认由 `ASSERTOR_DEFAULT_POLICY_*` 宏决定。
 */
enum class FailurePolicy
{
    Throw,
    Abort,
    LogOnly
};

// =====================================
// minimal internal logging backend
// =====================================
namespace detail
{
// 直接使用外部logstream的Formatter
using Formatter = kp::io::Formatter;

// 将外部LogStream适配为当前接口（构造参数与方法名稍有差异）
/**
 * @brief 日志输出流适配器（Assertor 内部）。
 * @details 该类将底层 `kp::io::LogStream` 适配为断言与异常框架可用的接口，提供：
 * - 前缀控制：用于标识模块或子系统来源；
 * - 输出策略：屏显与文件双轨；
 * - 可扩展 sink：通过 `Sinks` 将格式化文本扇出到其他消费端。
 * 设计初衷：
 * - 解耦：框架聚焦于异常信息组织，输出由独立组件实现；
 * - 可靠：高频路径零分配/低分配，避免在异常处理时影响性能；
 * - 一致：复用统一 Formatter，保证日志风格与字段一致性。
 */
class LogStream
{
public:
    /**
     * @brief 用户自定义 sink 集合代理。
     * @details 通过回调订阅格式化后的日志文本，实现轻耦合扇出：
     * - attach_sink：附加一个 `std::function<void(const std::string &)>` 回调。
     * - clear_sinks：清除全部附加的 sink。
     * - parent：所属 `LogStream` 指针，用于将操作回送到底层实现。
     */
    struct Sinks
    {
        void attach_sink(std::function<void(const std::string &)> sink)
        {
            parent->impl.sinks().attach_sink(std::move(sink));
        }
        void clear_sinks() { parent->impl.sinks().clear_sinks(); }
        LogStream * parent{};
    };

    /**
     * @brief 输出模式占位枚举，预留扩展。
     * @details 当前仅包含 `normal`；为未来异步/批量模式保留接口稳定性。
     */
    enum Mode
    {
        normal = 0
    };

    /**
     * @brief 构造日志输出流适配器。
     * @param prefix 文本前缀（如模块名）；为空时不输出前缀。
     * @param show_on_screen 是否输出到标准输出（便于调试）。
     * @param file_name 目标日志文件路径；为空表示暂不写文件。
     * @details 设计要点：
     * - 轻量构造：仅建立到底层 `kp::io::LogStream` 的桥接，不做 IO；
     * - 资源复用：通过 `formatter()` 代理共享格式器实例；
     * - 可扩展：结合 `Sinks` 实现扇出到自定义消费者（UI/远程收集等）。
     */
    LogStream(Mode, const char * prefix, bool show_on_screen, const char * file_name)
        : impl(kp::io::LogStream::normal,
               prefix ? std::string(prefix) : std::string{},
               show_on_screen,
               file_name ? std::string(file_name) : std::string{}),
          fmt_proxy_(impl.formatter())
    {
    }

    /**
     * @brief 获取格式器代理。
     * @return 引用到外部 `kp::io::Formatter`，用于按统一策略格式化日志消息。
     * @note 代理引用由底层流管理，避免重复构造提高性能。
     */
    Formatter & formatter() { return fmt_proxy_; }

    /**
     * @brief 获取 sink 集合代理。
     * @details 将订阅操作转发至底层实现的 `sinks()`，实现灵活的额外消费端。
     */
    Sinks & sinks()
    {
        sinks_proxy_.parent = this;
        return sinks_proxy_;
    }

    /** @brief 设置字段分隔符（默认空格）。*/
    void set_delimiter(char d) { impl.set_delimiter(d); }
    /** @brief 设置时区偏移（单位小时）。*/
    void set_timezone(int tz) { impl.set_timezone(tz); }
    /** @brief 控制是否在日志中包含线程 ID。*/
    void log_thread_id(bool enable) { impl.log_thread_id(enable); }
    /** @brief 清除文件输出 sink（保留屏显与自定义 sink）。*/
    void clear_file_sinks() { impl.clear_file_sinks(); }
    /**
     * @brief 设置日志输出文件。
     * @param path 文件路径。
     * @param append 是否追加写入；false 将截断重写。
     */
    void set_output_file(const std::string & path, bool append)
    {
        impl.set_output_file(path, append);
    }

#if __cplusplus >= 202002L
    /**
     * @brief 记录一条消息（含源位置）。
     * @param loc 源位置（C++20 `std::source_location`）。
     * @param message 文本消息。
     */
    void operator()(std::source_location loc, std::string_view message) { impl(loc, message); }
#endif

    /**
     * @brief 记录一条消息。
     * @param message 文本消息。
     */
    void operator()(const std::string & message) { impl(message); }

    /** 底层日志流：负责具体 IO 与格式策略。*/
    kp::io::LogStream impl;
    /** Formatter 代理：共享底层格式器以提升性能与一致性。*/
    Formatter fmt_proxy_;

private:
    /** Sinks 代理：维护对父对象的回送指针以进行转发。*/
    Sinks sinks_proxy_{};
};
} // namespace detail

// =====================================
// context: global runtime state
// =====================================
/**
 * @brief Assertor 全局运行时上下文。
 * @details 该结构体集中承载断言/异常框架的策略与日志配置，作为单例样式的全局状态：
 * - policy：失败时的处理策略（Throw/Abort/LogOnly），以 `atomic` 存储保证并发安全；
 * - log_enabled：日志开关，允许在运行期快速禁用；
 * - timezone_offset：日志时间的时区（小时偏移）；
 * - show_thread_id：是否在日志中包含线程 ID；
 * - log_file_path：当前日志文件路径（可选）；
 * - log_stream：日志流实现，封装底层写入与格式；
 * - stream_mutex：保护对 `log_stream` 的初始化与变更。
 * 设计要点：
 * - 线程安全：对策略与开关采用原子变量，减少锁竞争；
 * - 统一入口：所有公共 API 通过此上下文统一读写配置；
 * - 可配置性：支持通过环境变量与 API 在运行期调优。
 */
struct AssertorContext
{
    std::atomic<FailurePolicy> policy{FailurePolicy::Throw};
    std::atomic<bool> log_enabled{true};

    int timezone_offset{0};
    bool show_thread_id{false};
    std::optional<std::string> log_file_path{};

    std::shared_ptr<detail::LogStream> log_stream{};

    std::mutex stream_mutex;
};

inline void init_from_environment();

namespace
{
static AssertorContext * g_ctx_ptr = nullptr;

static AssertorContext &
default_context()
{
    static AssertorContext ctx;
    if (!ctx.log_stream)
    {
        auto ls = std::make_shared<detail::LogStream>(detail::LogStream::normal,
                                                      /*prefix=*/"assertor",
                                                      /*show_on_screen=*/false,
                                                      /*file_name=*/"");
        ls->set_delimiter(' ');
        ctx.log_stream = std::move(ls);

        // 默认值：策略与日志配置
        ctx.policy.store(FailurePolicy::Throw,
                         std::memory_order_relaxed);            // ASSERTOR_POLICY=LogOnly
        ctx.log_enabled.store(true, std::memory_order_relaxed); // ASSERTOR_LOG_ENABLE=True
        ctx.timezone_offset = 8;                                // ASSERTOR_LOG_TZ=8
        ctx.show_thread_id = false;                             // ASSERTOR_LOG_THREAD_ID=False
        ctx.log_stream->set_timezone(ctx.timezone_offset);
        ctx.log_stream->log_thread_id(ctx.show_thread_id);

        // 默认日志文件（命令行运行未设置环境变量时写入当前工作目录）
        if (!ctx.log_file_path.has_value())
        {
            ctx.log_file_path = std::string{"kp.log"};
            ctx.log_stream->clear_file_sinks();
            ctx.log_stream->set_output_file(*ctx.log_file_path, /*append=*/true);
        }

        // 自动从环境变量初始化（若存在则覆盖上述默认值）
        init_from_environment();
    }
    return ctx;
}
}

/**
 * @brief 获取当前上下文（若未设定则返回默认单例）。
 * @details 若存在用户设置的 `g_ctx_ptr`，则返回其内容；否则初始化默认上下文：
 * - 延迟初始化 `log_stream` 以避免静态初始化次序问题；
 * - 设置标准分隔符，保证日志格式一致性。
 */
inline AssertorContext &
get_context()
{
    AssertorContext * p = g_ctx_ptr;
    if (p == nullptr)
        return default_context();
    if (!p->log_stream)
    {
        auto ls = std::make_shared<detail::LogStream>(detail::LogStream::normal,
                                                      /*prefix=*/"assertor",
                                                      /*show_on_screen=*/false,
                                                      /*file_name=*/"");
        ls->set_delimiter(' ');
        p->log_stream = std::move(ls);
    }
    return *p;
}

/**
 * @brief 设置当前上下文指针。
 * @param ctx 用户创建或外部托管的上下文。
 * @note 不接管所有权；调用者需保证其生命周期覆盖使用期。
 */
inline void
set_context(AssertorContext * ctx)
{
    g_ctx_ptr = ctx;
}

// =====================================
// policy API
// =====================================

/** @brief 读取默认失败策略（原子读）。*/
inline FailurePolicy
get_default_policy()
{
    return get_context().policy.load(std::memory_order_relaxed);
}
/** @brief 设置默认失败策略（原子写）。*/
inline void
set_default_policy(FailurePolicy policy)
{
    get_context().policy.store(policy, std::memory_order_relaxed);
}
/** @brief 查询日志是否启用（原子读）。*/
inline bool
is_logging_enabled()
{
    return get_context().log_enabled.load(std::memory_order_relaxed);
}
/** @brief 启用/禁用日志（原子写）。*/
inline void
set_logging_enabled(bool enable)
{
    get_context().log_enabled.store(enable, std::memory_order_relaxed);
}

/**
 * @brief 向日志系统输出一条格式化消息。
 * @param message 文本消息。
 * @param where 源位置，用于定位调用点。
 * @details 根据 `FailurePolicy` 设置日志级别：Throw/Abort 为较高等级；
 * 在日志开关关闭时直接跳过。C++20 下使用 `std::source_location` 增强可定位性。
 */
namespace detail
{
inline void
emit_log(std::string_view message, SourceLocation where)
{
    auto & ctx = get_context();
    if (!ctx.log_enabled.load(std::memory_order_relaxed))
        return;
    auto policy = ctx.policy.load(std::memory_order_relaxed);
    int level = (policy == FailurePolicy::Abort || policy == FailurePolicy::Throw) ? 2 : 1;
    ctx.log_stream->formatter().set_level(level);
#if __cplusplus >= 202002L
    ctx.log_stream->operator()(where, message);
#else
    ctx.log_stream->formatter().set_source(
        where.file_name(), static_cast<int>(where.line()), where.function_name());
    ctx.log_stream->formatter().enable_source(true);
    (*ctx.log_stream)(std::string(message));
#endif
}
} // namespace detail

/**
 * @brief 设置日志文件输出。
 * @param path 文件路径；为空则关闭文件输出。
 * @param append 是否以追加模式写入（默认 true）。
 * @details 线程安全：持有 `stream_mutex`；重置文件 sink 并按需打开新文件。
 */
inline void
set_log_file(const std::string & path, bool append = true)
{
    auto & ctx = get_context();
    std::scoped_lock lk(ctx.stream_mutex);
    ctx.log_file_path = path.empty() ? std::nullopt : std::optional<std::string>(path);
    ctx.log_stream->clear_file_sinks();
    if (!path.empty())
        ctx.log_stream->set_output_file(path, append);
}

/**
 * @brief 清除日志文件输出。
 * @details 线程安全：持有 `stream_mutex`；移除文件 sinks 并重置路径。
 */
inline void
clear_log_file()
{
    auto & ctx = get_context();
    std::scoped_lock lk(ctx.stream_mutex);
    ctx.log_file_path = std::nullopt;
    ctx.log_stream->clear_file_sinks();
}

/**
 * @brief 设置日志时区偏移（单位：小时）。
 * @param offset_hours 时区偏移量，例如 +8 表示东八区。
 */
inline void
set_log_timezone(int offset_hours)
{
    auto & ctx = get_context();
    std::scoped_lock lk(ctx.stream_mutex);
    ctx.timezone_offset = offset_hours;
    ctx.log_stream->set_timezone(offset_hours);
}

/**
 * @brief 控制日志中是否输出线程 ID。
 * @param enable 为 true 时输出线程 ID，为 false 时不输出。
 */
inline void
set_log_thread_id_enabled(bool enable)
{
    auto & ctx = get_context();
    std::scoped_lock lk(ctx.stream_mutex);
    ctx.show_thread_id = enable;
    ctx.log_stream->log_thread_id(enable);
}

/**
 * @brief 附加一个日志 sink 回调。
 * @param sink 回调函数，接收格式化后的完整日志文本。
 * @details 线程安全：持有 `stream_mutex`；可用于转发到自定义目的地（如 GUI、网络）。
 */
inline void
attach_log_sink(std::function<void(const std::string &)> sink)
{
    auto & ctx = get_context();
    std::scoped_lock lk(ctx.stream_mutex);
    ctx.log_stream->sinks().attach_sink(std::move(sink));
}

/**
 * @brief 清除所有日志 sink 回调。
 * @details 线程安全：持有 `stream_mutex`；不影响文件/屏显的内置输出配置。
 */
inline void
clear_log_sinks()
{
    auto & ctx = get_context();
    std::scoped_lock lk(ctx.stream_mutex);
    ctx.log_stream->sinks().clear_sinks();
}

namespace
{
inline bool
parse_bool(std::string_view text)
{
    std::string upper{text};
    for (auto & ch : upper)
        ch = static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
    return upper == "1" || upper == "TRUE" || upper == "ON" || upper == "YES";
}

inline bool
parse_policy(std::string_view value, FailurePolicy & out)
{
    std::string s(value);
    for (auto & ch : s)
        ch = static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
    if (s == "THROW")
    {
        out = FailurePolicy::Throw;
        return true;
    }
    if (s == "ABORT")
    {
        out = FailurePolicy::Abort;
        return true;
    }
    if (s == "LOGONLY" || s == "LOG_ONLY" || s == "LOG")
    {
        out = FailurePolicy::LogOnly;
        return true;
    }
    return false;
}

inline std::string
getenv_string(const char * name)
{
    if (const char * v = std::getenv(name))
        return std::string(v);
    return {};
}
}

/**
 * @brief 从环境变量初始化框架配置。
 * @details 支持变量：
 * - `ASSERTOR_POLICY`：取值 THROW/ABORT/LOGONLY；
 * - `ASSERTOR_LOG_ENABLE`：布尔；
 * - `ASSERTOR_LOG_FILE`：路径（空则忽略）；
 * - `ASSERTOR_LOG_TZ`：整数时区偏移小时；
 * - `ASSERTOR_LOG_THREAD_ID`：布尔，是否显示线程 ID。
 */
inline void
init_from_environment()
{
    if (auto policy = getenv_string("ASSERTOR_POLICY"); !policy.empty())
    {
        FailurePolicy p;
        if (parse_policy(policy, p))
            set_default_policy(p);
    }
    if (auto enable = getenv_string("ASSERTOR_LOG_ENABLE"); !enable.empty())
        set_logging_enabled(parse_bool(enable));
    if (auto file = getenv_string("ASSERTOR_LOG_FILE"); !file.empty())
        set_log_file(file, true);
    if (auto tz = getenv_string("ASSERTOR_LOG_TZ"); !tz.empty())
    {
        int offset = std::strtol(tz.c_str(), nullptr, 10);
        set_log_timezone(offset);
    }
    if (auto tid = getenv_string("ASSERTOR_LOG_THREAD_ID"); !tid.empty())
        set_log_thread_id_enabled(parse_bool(tid));
}

// =====================================
// exceptions and stacktrace
// =====================================

/**
 * @brief 捕获当前线程的堆栈文本。
 * @param max_frames 捕获的最大帧数上限。
 * @return 可读文本，若不可用或禁用则为空。
 * @details
 * - `std::stacktrace` 可用时输出文件/行号/符号；
 * - POSIX 平台回退使用 `backtrace` + `dladdr` + `cxa_demangle`；
 * - 不支持或失败时返回空字符串。
 */
inline std::string
capture_stacktrace(std::size_t max_frames = 64)
{
#if defined(ASSERTOR_ENABLE_STACKTRACE) && ASSERT_HAS_STD_STACKTRACE
    try
    {
        std::stacktrace st = std::stacktrace::current();
        std::ostringstream os;
        std::size_t count = 0;
        for (auto const & f : st)
        {
            if (count++ >= max_frames)
                break;
            os << "  #" << count << " " << f.source_file() << ':' << f.source_line() << " | "
               << f.description() << '\n';
        }
        return os.str();
    }
    catch (...)
    {
        return {};
    }
#elif defined(ASSERTOR_ENABLE_STACKTRACE) && ASSERT_HAS_POSIX_BACKTRACE
    try
    {
        constexpr std::size_t k_max_capture = 256;
        std::size_t capture_count = std::min(max_frames, k_max_capture);
        std::vector<void *> buffer(capture_count);
        int n = ::backtrace(buffer.data(), static_cast<int>(buffer.size()));
        std::ostringstream os;
        for (int i = 0; i < n; ++i)
        {
            Dl_info info{};
            const char * demangled = nullptr;
            int status = 0;
            if (::dladdr(buffer[i], &info) != 0 && info.dli_sname)
            {
                demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
            }
            os << "  #" << (i + 1) << " addr=" << buffer[i];
            if (info.dli_fname)
                os << " | lib=" << info.dli_fname;
            os << " | func="
               << (demangled && status == 0 ? demangled
                                            : (info.dli_sname ? info.dli_sname : "<unknown>"))
               << '\n';
            if (demangled)
                std::free(const_cast<char *>(demangled));
        }
        return os.str();
    }
    catch (...)
    {
        return {};
    }
#else
    (void)max_frames;
    return {};
#endif
}

/**
 * @brief 异常基类，封装消息、条件与源位置。
 * @details 构造时可捕获栈；延迟格式化 `what()` 并缓存；派生类通过 `build_info()` 追加信息。
 */
class ExceptionBase : public std::exception
{
public:
    ExceptionBase(std::string_view message,
                  std::string_view condition,
                  SourceLocation where = SourceLocation::current())
        : _message(message), _condition(condition), _where(where)
    {
        _stacktrace_text = capture_stacktrace();
    }

    const char * what() const noexcept override
    {
        if (_formatted.empty())
        {
            std::ostringstream os;
            os << "Exception at " << _where.file_name() << ':' << _where.line() << " in "
               << _where.function_name() << '\n';
            if (!_condition.empty())
                os << "  condition: " << _condition << '\n';
            os << "  message: " << _message << '\n';
            if (!_stacktrace_text.empty())
                os << "  stacktrace:\n" << _stacktrace_text;
            build_info(_formatted);
            _formatted = os.str() + _formatted;
        }
        return _formatted.c_str();
    }

    const std::string & message() const noexcept { return _message; }
    std::string_view condition() const noexcept { return _condition; }
    SourceLocation where() const noexcept { return _where; }
    const std::string & stacktrace() const noexcept { return _stacktrace_text; }
    void print(std::ostream & out, bool include_stacktrace = true) const
    {
        out << "Exception at " << _where.file_name() << ':' << _where.line() << " in "
            << _where.function_name() << '\n';
        if (!_condition.empty())
            out << "  condition: " << _condition << '\n';
        out << "  message: " << _message << '\n';
        std::string extra;
        build_info(extra);
        out << extra;
        if (include_stacktrace && !_stacktrace_text.empty())
            out << "  stacktrace:\n" << _stacktrace_text;
    }

protected:
    virtual void build_info(std::string & out) const { (void)out; }

private:
    std::string _message;
    std::string _condition;
    SourceLocation _where;
    mutable std::string _formatted;
    std::string _stacktrace_text;
};

/**
 * @brief 打印异常到输出流。
 * @param out 目标输出流。
 * @param include_stacktrace 是否包含栈信息。
 */
inline void
print_exception(std::ostream & out, const ExceptionBase & ex, bool include_stacktrace = true)
{
    ex.print(out, include_stacktrace);
}

/**
 * @brief 通过日志通道输出异常信息。
 * @details 利用内部日志通道，将异常与源位置关联输出，便于统一处理。
 */
inline void
emit_exception(const ExceptionBase & ex, SourceLocation where)
{
    detail::emit_log(ex.what(), where);
}

namespace detail
{
inline void
append_exception_type(std::string & out, const char * type_name)
{
    out += "  type: ";
    out += type_name;
    out += '\n';
}

template <typename T>
inline void
append_exception_field(std::string & out, const char * field_name, const T & value)
{
    std::ostringstream os;
    os << "  " << field_name << '=' << stringify_value(value) << '\n';
    out += os.str();
}
} // namespace detail

/**
 * @brief 未实现功能异常。
 * @details 用于标记占位或未完成的功能路径。
 */
class NotImplemented : public ExceptionBase
{
public:
    NotImplemented(std::string_view message, SourceLocation where = SourceLocation::current())
        : ExceptionBase(message, {}, where)
    {
    }
    void build_info(std::string & out) const override
    {
        detail::append_exception_type(out, "NotImplemented");
    }
};

/**
 * @brief 断言失败异常。
 * @details 由 `ASSERT_ENSURE` 等宏触发，携带失败条件文本与源位置信息。
 */
class AssertionFailed : public ExceptionBase
{
public:
    AssertionFailed(std::string_view message,
                    std::string_view condition,
                    SourceLocation where = SourceLocation::current())
        : ExceptionBase(message, condition, where)
    {
    }
    AssertionFailed(std::string_view message, SourceLocation where, std::string_view condition = {})
        : ExceptionBase(message, condition, where)
    {
    }
    void build_info(std::string & out) const override
    {
        detail::append_exception_type(out, "AssertionFailed");
    }
};

namespace detail
{
inline void
emit_failure_message(const std::string & msg, SourceLocation where)
{
    emit_log(msg, where);
}
} // namespace detail

// Failure dispatch core
template <typename ExceptionFactory>
inline void
handle_exception_condition(bool condition,
                           std::string_view condition_text,
                           SourceLocation where,
                           ExceptionFactory && factory)
{
#if defined(ASSERTOR_ENABLE_ASSERTS)
    if (condition)
        return;
    auto ex = std::forward<ExceptionFactory>(factory)(where, condition_text);
    switch (get_default_policy())
    {
        case FailurePolicy::Throw:
            throw ex;
        case FailurePolicy::Abort:
            detail::emit_log(ex.what(), where);
            std::abort();
        case FailurePolicy::LogOnly:
            detail::emit_log(ex.what(), where);
            break;
    }
#else
    (void)condition;
    (void)condition_text;
    (void)where;
    (void)factory;
#endif
}

// Exception factory helper
template <typename ExceptionType, typename... Args>
inline ExceptionType
make_exception_with_context(SourceLocation where, std::string_view condition_text, Args &&... args)
{
    return ExceptionType(std::forward<Args>(args)..., where, condition_text);
}

namespace detail
{
template <typename T>
struct NamedField
{
    const char * name;
    T value;
    using value_type = T;
};

template <typename U>
inline NamedField<std::decay_t<U>>
make_field(const char * name, U && v)
{
    return NamedField<std::decay_t<U>>{name, std::forward<U>(v)};
}

template <typename... Fields>
class ExceptionWithNamedFields : public ExceptionBase
{
public:
    template <typename... UFields,
              typename = std::enable_if_t<sizeof...(UFields) == sizeof...(Fields)>>
    ExceptionWithNamedFields(const char * type_name,
                             std::string_view message_literal,
                             std::string_view cond_text,
                             SourceLocation where,
                             UFields &&... ufields)
        : ExceptionBase(message_literal, cond_text, where),
          _type_name(type_name),
          _fields(convert_field<Fields>(std::forward<UFields>(ufields))...)
    {
    }

    void build_info(std::string & out) const override
    {
        detail::append_exception_type(out, _type_name);
        append_fields(out, std::index_sequence_for<Fields...>{});
    }

private:
    const char * _type_name;
    std::tuple<Fields...> _fields;

    template <typename TargetField, typename SrcField>
    static TargetField convert_field(SrcField && src)
    {
        using TargetValue = typename TargetField::value_type;
        return TargetField{src.name, TargetValue(std::forward<decltype(src.value)>(src.value))};
    }

    template <std::size_t... I>
    void append_fields(std::string & out, std::index_sequence<I...>) const
    {
        (detail::append_exception_field(out, std::get<I>(_fields).name, std::get<I>(_fields).value),
         ...);
    }
};
} // namespace detail

#define ASSERT_DECL_EXCEPTION0(name, message_literal)                                              \
    class name : public ExceptionBase                                                              \
    {                                                                                              \
    public:                                                                                        \
        /**                                                                                        \
         * @brief 默认构造：使用固定消息文本。                                                     \
         * @param where 源位置（默认当前）。                                                        \
         * @param cond_text 触发条件文本（默认空）。                                                \
         * @throws 不抛出额外异常；具体抛出/中止/仅日志由 FailurePolicy 决定。                      \
         */                                                                                        \
        explicit name(SourceLocation where = SourceLocation::current(),                            \
                      std::string_view cond_text = {})                                             \
            : ExceptionBase(message_literal, cond_text, where)                                     \
        {                                                                                          \
        }                                                                                          \
        /**                                                                                        \
         * @brief 使用自定义消息构造异常。                                                          \
         * @param message 自定义消息文本（附加到固定文本之后）。                                    \
         * @param where 源位置（默认当前）。                                                        \
         * @param cond_text 触发条件文本（默认空）。                                                \
         * @throws 不抛出额外异常；具体抛出/中止/仅日志由 FailurePolicy 决定。                      \
         * @details 最终消息格式为：`message_literal: message`；若 `message` 为空则仅使用固定文本。 \
         */                                                                                        \
        explicit name(std::string_view message,                                                     \
                      SourceLocation where = SourceLocation::current(),                            \
                      std::string_view cond_text = {})                                             \
            : ExceptionBase(                                                                       \
                  [&]() {                                                                          \
                      if (message.empty())                                                         \
                          return std::string{message_literal};                                     \
                      std::string combined;                                                        \
                      combined.reserve(std::size(message_literal) + 2 + message.size());           \
                      combined.append(message_literal);                                            \
                      combined.append(": ");                                                      \
                      combined.append(message);                                                    \
                      return combined;                                                             \
                  }(),                                                                             \
                  cond_text,                                                                       \
                  where)                                                                           \
        {                                                                                          \
        }                                                                                          \
        void build_info(std::string & out) const override                                          \
        {                                                                                          \
            detail::append_exception_type(out, #name);                                             \
        }                                                                                          \
    };

#define ASSERT_DECL_EXCEPTION1(name, message_literal, T1, F1)                                      \
    class name : public detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>>     \
    {                                                                                              \
    public:                                                                                        \
        template <typename U1>                                                                     \
        explicit name(U1 && F1##_value,                                                            \
                      SourceLocation where = SourceLocation::current(),                            \
                      std::string_view cond_text = {})                                             \
            : detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>>(              \
                  #name,                                                                           \
                  message_literal,                                                                 \
                  cond_text,                                                                       \
                  where,                                                                           \
                  detail::make_field(#F1, std::forward<U1>(F1##_value)))                           \
        {                                                                                          \
        }                                                                                          \
    };

#define ASSERT_DECL_EXCEPTION2(name, message_literal, T1, F1, T2, F2)                              \
    class name : public detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>,     \
                                                         detail::NamedField<std::decay_t<T2>>>     \
    {                                                                                              \
    public:                                                                                        \
        template <typename U1, typename U2>                                                        \
        explicit name(U1 && F1##_value,                                                            \
                      U2 && F2##_value,                                                            \
                      SourceLocation where = SourceLocation::current(),                            \
                      std::string_view cond_text = {})                                             \
            : detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>,               \
                                               detail::NamedField<std::decay_t<T2>>>(              \
                  #name,                                                                           \
                  message_literal,                                                                 \
                  cond_text,                                                                       \
                  where,                                                                           \
                  detail::make_field(#F1, std::forward<U1>(F1##_value)),                           \
                  detail::make_field(#F2, std::forward<U2>(F2##_value)))                           \
        {                                                                                          \
        }                                                                                          \
    };

#define ASSERT_DECL_EXCEPTION3(name, message_literal, T1, F1, T2, F2, T3, F3)                      \
    class name : public detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>,     \
                                                         detail::NamedField<std::decay_t<T2>>,     \
                                                         detail::NamedField<std::decay_t<T3>>>     \
    {                                                                                              \
    public:                                                                                        \
        template <typename U1, typename U2, typename U3>                                           \
        explicit name(U1 && F1##_value,                                                            \
                      U2 && F2##_value,                                                            \
                      U3 && F3##_value,                                                            \
                      SourceLocation where = SourceLocation::current(),                            \
                      std::string_view cond_text = {})                                             \
            : detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>,               \
                                               detail::NamedField<std::decay_t<T2>>,               \
                                               detail::NamedField<std::decay_t<T3>>>(              \
                  #name,                                                                           \
                  message_literal,                                                                 \
                  cond_text,                                                                       \
                  where,                                                                           \
                  detail::make_field(#F1, std::forward<U1>(F1##_value)),                           \
                  detail::make_field(#F2, std::forward<U2>(F2##_value)),                           \
                  detail::make_field(#F3, std::forward<U3>(F3##_value)))                           \
        {                                                                                          \
        }                                                                                          \
    };

#define ASSERT_DECL_EXCEPTION4(name, message_literal, T1, F1, T2, F2, T3, F3, T4, F4)              \
    class name : public detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>,     \
                                                         detail::NamedField<std::decay_t<T2>>,     \
                                                         detail::NamedField<std::decay_t<T3>>,     \
                                                         detail::NamedField<std::decay_t<T4>>>     \
    {                                                                                              \
    public:                                                                                        \
        template <typename U1, typename U2, typename U3, typename U4>                              \
        explicit name(U1 && F1##_value,                                                            \
                      U2 && F2##_value,                                                            \
                      U3 && F3##_value,                                                            \
                      U4 && F4##_value,                                                            \
                      SourceLocation where = SourceLocation::current(),                            \
                      std::string_view cond_text = {})                                             \
            : detail::ExceptionWithNamedFields<detail::NamedField<std::decay_t<T1>>,               \
                                               detail::NamedField<std::decay_t<T2>>,               \
                                               detail::NamedField<std::decay_t<T3>>,               \
                                               detail::NamedField<std::decay_t<T4>>>(              \
                  #name,                                                                           \
                  message_literal,                                                                 \
                  cond_text,                                                                       \
                  where,                                                                           \
                  detail::make_field(#F1, std::forward<U1>(F1##_value)),                           \
                  detail::make_field(#F2, std::forward<U2>(F2##_value)),                           \
                  detail::make_field(#F3, std::forward<U3>(F3##_value)),                           \
                  detail::make_field(#F4, std::forward<U4>(F4##_value)))                           \
        {                                                                                          \
        }                                                                                          \
    };

// =====================================
// assertions core and macros
// =====================================
namespace
{
// callable returns std::string detection
template <typename Msg, typename = void>
struct msg_returns_string : std::false_type
{
};

template <typename Msg>
struct msg_returns_string<Msg, std::void_t<decltype(std::declval<Msg>()())>>
    : std::integral_constant<bool,
                             std::is_convertible_v<decltype(std::declval<Msg>()()), std::string>>
{
};

template <typename Msg>
constexpr bool msg_returns_string_v = msg_returns_string<Msg>::value;

template <typename T, typename = void>
struct convertible_to_long_double : std::false_type
{
};

template <typename T>
struct convertible_to_long_double<
    T,
    std::void_t<decltype(static_cast<long double>(std::declval<T>()))>> : std::true_type
{
};

template <typename T>
constexpr bool convertible_to_long_double_v = convertible_to_long_double<T>::value;
}

// Helper: check finiteness only for floating-point types
template <typename T>
inline bool
is_finite_scalar(const T & v)
{
    if constexpr (std::is_floating_point_v<std::decay_t<T>>)
        return std::isfinite(v);
    else
        return true;
}

// Helper: annotate field name with original type name
template <typename T>
inline std::string
annotate_name_with_type(std::string name, const T &)
{
    using U = std::decay_t<T>;
    name += "<T=";
    name += demangle_type_name(typeid(U).name());
    name += '>';
    return name;
}

template <typename Msg>
inline void
assert_condition(bool condition,
                 const Msg & make_message,
                 std::string_view condition_text,
                 SourceLocation where)
{
#if defined(ASSERTOR_ENABLE_ASSERTS)
    auto to_string = [&]() -> std::string {
        if constexpr (msg_returns_string_v<Msg>)
        {
            return make_message();
        }
        else if constexpr (std::is_convertible_v<Msg, std::string>)
        {
            return static_cast<std::string>(make_message);
        }
        else if constexpr (std::is_convertible_v<Msg, std::string_view>)
        {
            return std::string(static_cast<std::string_view>(make_message));
        }
        else if constexpr (std::is_same_v<std::decay_t<Msg>, const char *> ||
                           std::is_same_v<std::decay_t<Msg>, char *>)
        {
            return make_message ? std::string(make_message) : std::string{};
        }
        else
        {
            return std::string{};
        }
    };
    handle_exception_condition(
        condition,
        condition_text,
        where,
        [&](SourceLocation loc, std::string_view cond_text)
        { return AssertionFailed(to_string(), loc, cond_text); });
#else
    (void)condition;
    (void)make_message;
    (void)condition_text;
    (void)where;
#endif
}

template <typename L, typename R, typename Msg>
inline std::string
build_compare_message(std::string_view op_text,
                      const L & lhs,
                      const R & rhs,
                      const Msg & make_message)
{
    std::ostringstream os;
    os << "compare failed: (" << op_text << ")"
       << ", left<T=" << demangle_type_name(typeid(L).name()) << ">=" << stringify_value(lhs)
       << ", right<U=" << demangle_type_name(typeid(R).name()) << ">=" << stringify_value(rhs);
    if constexpr (msg_returns_string_v<Msg>)
    {
        auto extra = make_message();
        if (!extra.empty())
            os << ", extra=" << extra;
    }
    else if constexpr (std::is_convertible_v<Msg, std::string>)
    {
        auto extra = static_cast<std::string>(make_message);
        if (!extra.empty())
            os << ", extra=" << extra;
    }
    else if constexpr (std::is_convertible_v<Msg, std::string_view>)
    {
        auto extra = static_cast<std::string_view>(make_message);
        if (!extra.empty())
            os << ", extra=" << extra;
    }
    else if constexpr (std::is_same_v<std::decay_t<Msg>, const char *> ||
                       std::is_same_v<std::decay_t<Msg>, char *>)
    {
        auto p = make_message;
        if (p && *p)
            os << ", extra=" << p;
    }
    return os.str();
}

template <typename Op, typename L, typename R, typename Msg>
inline void
assert_compare(L const & lhs, R const & rhs, Msg const & make_message, SourceLocation where)
{
#if defined(ASSERTOR_ENABLE_ASSERTS)
    handle_exception_condition(
        Op::apply(lhs, rhs),
        Op::op_text,
        where,
        [&](SourceLocation loc, std::string_view cond_text)
        {
            return AssertionFailed(
                build_compare_message(Op::op_text, lhs, rhs, make_message), loc, cond_text);
        });
#else
    (void)lhs;
    (void)rhs;
    (void)make_message;
    (void)where;
#endif
}

template <typename L, typename R, typename Msg>
inline void
assert_near(
    L const & lhs, R const & rhs, double tolerance, Msg const & make_message, SourceLocation where)
{
    static_assert(convertible_to_long_double_v<L> && convertible_to_long_double_v<R>,
                  "assert_near requires lhs/rhs convertible to long double");
#if defined(ASSERTOR_ENABLE_ASSERTS)
    const long double dl = static_cast<long double>(lhs);
    const long double dr = static_cast<long double>(rhs);
    const long double tol = static_cast<long double>(tolerance);
    const bool invalid = tol < 0 || !std::isfinite(tol) || !std::isfinite(dl) || !std::isfinite(dr);
    const long double diff = std::fabs(dl - dr);

    auto get_user_msg = [&]() -> std::string {
        if constexpr (msg_returns_string_v<Msg>)
            return make_message();
        else if constexpr (std::is_convertible_v<Msg, std::string>)
            return static_cast<std::string>(make_message);
        else if constexpr (std::is_convertible_v<Msg, std::string_view>)
            return std::string(static_cast<std::string_view>(make_message));
        else if constexpr (std::is_same_v<std::decay_t<Msg>, const char *> ||
                           std::is_same_v<std::decay_t<Msg>, char *>)
            return make_message ? std::string(make_message) : std::string{};
        else
            return std::string{};
    };
    auto make_combined = [&](std::string const & extra_info)
    {
        std::string user_msg = get_user_msg();
        if (user_msg.empty())
            return extra_info;
        if (extra_info.empty())
            return user_msg;
        return extra_info + "; " + user_msg;
    };

    std::string internal_extra;
    if (invalid)
    {
        std::ostringstream extra;
        extra << "invalid near-compare: tolerance=" << static_cast<double>(tol)
              << ", lhs=" << static_cast<double>(dl) << ", rhs=" << static_cast<double>(dr);
        internal_extra = extra.str();
    }
    else
    {
        std::ostringstream extra;
        extra << "tolerance=" << static_cast<double>(tol) << ", diff=" << static_cast<double>(diff);
        internal_extra = extra.str();
    }

    auto pred = [&](const L &, const R &) { return !invalid && (diff <= tol); };

    auto op_text = "|lhs - rhs| <= tolerance";
    auto msg_factory = [&] {
        return build_compare_message(
            op_text, lhs, rhs, [&] { return make_combined(internal_extra); });
    };
    handle_exception_condition(pred(lhs, rhs),
                               op_text,
                               where,
                               [&](SourceLocation loc, std::string_view cond_text)
                               { return AssertionFailed(msg_factory(), loc, cond_text); });
#else
    (void)lhs;
    (void)rhs;
    (void)tolerance;
    (void)make_message;
    (void)where;
#endif
}

struct OpEq
{
    static constexpr std::string_view op_text = "lhs == rhs";
    template <typename L, typename R>
    static inline bool apply(const L & lhs, const R & rhs)
    {
        return lhs == rhs;
    }
};
struct OpNe
{
    static constexpr std::string_view op_text = "lhs != rhs";
    template <typename L, typename R>
    static inline bool apply(const L & lhs, const R & rhs)
    {
        return !(lhs == rhs);
    }
};
struct OpLt
{
    static constexpr std::string_view op_text = "lhs < rhs";
    template <typename L, typename R>
    static inline bool apply(const L & lhs, const R & rhs)
    {
        return lhs < rhs;
    }
};
struct OpLe
{
    static constexpr std::string_view op_text = "lhs <= rhs";
    template <typename L, typename R>
    static inline bool apply(const L & lhs, const R & rhs)
    {
        return lhs <= rhs;
    }
};
struct OpGt
{
    static constexpr std::string_view op_text = "lhs > rhs";
    template <typename L, typename R>
    static inline bool apply(const L & lhs, const R & rhs)
    {
        return lhs > rhs;
    }
};
struct OpGe
{
    static constexpr std::string_view op_text = "lhs >= rhs";
    template <typename L, typename R>
    static inline bool apply(const L & lhs, const R & rhs)
    {
        return lhs >= rhs;
    }
};

#define ASSERT(cond, make_msg)                                                                     \
    assert_condition((cond), (make_msg), #cond, ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT0(cond)                                                                              \
    assert_condition((cond),                                                                       \
                     [] { return std::string{"assert failed: " #cond}; },                          \
                     #cond,                                                                        \
                     ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_EQ(lhs, rhs, make_msg)                                                              \
    assert_compare<OpEq>((lhs), (rhs), (make_msg), ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT_EQ0(lhs, rhs)                                                                       \
    assert_compare<OpEq>((lhs),                                                                    \
                         (rhs),                                                                    \
                         [] { return std::string{"expected: " #lhs " == " #rhs}; },                \
                         ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_NE(lhs, rhs, make_msg)                                                              \
    assert_compare<OpNe>((lhs), (rhs), (make_msg), ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT_NE0(lhs, rhs)                                                                       \
    assert_compare<OpNe>((lhs),                                                                    \
                         (rhs),                                                                    \
                         [] { return std::string{"expected: " #lhs " != " #rhs}; },                \
                         ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_LT(lhs, rhs, make_msg)                                                              \
    assert_compare<OpLt>((lhs), (rhs), (make_msg), ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT_LT0(lhs, rhs)                                                                       \
    assert_compare<OpLt>((lhs),                                                                    \
                         (rhs),                                                                    \
                         [] { return std::string{"expected: " #lhs " < " #rhs}; },                 \
                         ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_LE(lhs, rhs, make_msg)                                                              \
    assert_compare<OpLe>((lhs), (rhs), (make_msg), ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT_LE0(lhs, rhs)                                                                       \
    assert_compare<OpLe>((lhs),                                                                    \
                         (rhs),                                                                    \
                         [] { return std::string{"expected: " #lhs " <= " #rhs}; },                \
                         ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_GT(lhs, rhs, make_msg)                                                              \
    assert_compare<OpGt>((lhs), (rhs), (make_msg), ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT_GT0(lhs, rhs)                                                                       \
    assert_compare<OpGt>((lhs),                                                                    \
                         (rhs),                                                                    \
                         [] { return std::string{"expected: " #lhs " > " #rhs}; },                 \
                         ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_GE(lhs, rhs, make_msg)                                                              \
    assert_compare<OpGe>((lhs), (rhs), (make_msg), ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT_GE0(lhs, rhs)                                                                       \
    assert_compare<OpGe>((lhs),                                                                    \
                         (rhs),                                                                    \
                         [] { return std::string{"expected: " #lhs " >= " #rhs}; },                \
                         ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_NEAR(lhs, rhs, tol, make_msg)                                                       \
    assert_near((lhs), (rhs), (tol), (make_msg), ASSERT_CURRENT_SOURCE_LOCATION)
#define ASSERT_NEAR0(lhs, rhs, tol)                                                                \
    assert_near((lhs),                                                                             \
                (rhs),                                                                             \
                (tol),                                                                             \
                [] { return std::string{"expected: |" #lhs " - " #rhs "| <= " #tol}; },            \
                ASSERT_CURRENT_SOURCE_LOCATION)

#define ASSERT_ENSURE(condition, exception_type, ...)                                              \
    handle_exception_condition(                                                                    \
        (condition),                                                                               \
        #condition,                                                                                \
        ASSERT_CURRENT_SOURCE_LOCATION,                                                            \
        [&](SourceLocation loc, std::string_view cond_text)                                        \
        { return make_exception_with_context<exception_type>(loc, cond_text, ##__VA_ARGS__); })

#define ASSERT_THROW_IF(condition, exception_type, ...)                                            \
    handle_exception_condition(                                                                    \
        !(condition),                                                                              \
        #condition,                                                                                \
        ASSERT_CURRENT_SOURCE_LOCATION,                                                            \
        [&](SourceLocation loc, std::string_view cond_text)                                        \
        { return make_exception_with_context<exception_type>(loc, cond_text, ##__VA_ARGS__); })

#define ASSERT_IN_CLOSED_RANGE(val, minv, maxv)                                                    \
    ASSERT_ENSURE(((val) >= (minv) && (val) <= (maxv)),                                            \
                  ScalarOutOfRange,                                                                \
                  annotate_name_with_type(std::string{#val}, (val)),                               \
                  stringify_value(val),                                                            \
                  stringify_value(minv),                                                           \
                  stringify_value(maxv))

#define ASSERT_IN_OPEN_RANGE(val, minv, maxv)                                                      \
    ASSERT_ENSURE(((val) > (minv) && (val) < (maxv)),                                              \
                  ScalarOutOfRange,                                                                \
                  annotate_name_with_type(std::string{#val}, (val)),                               \
                  stringify_value(val),                                                            \
                  stringify_value(minv),                                                           \
                  stringify_value(maxv))

#define ASSERT_GE_SCALAR(val, ref)                                                                 \
    handle_exception_condition(OpGe::apply((val), (ref)),                                          \
                               OpGe::op_text,                                                      \
                               ASSERT_CURRENT_SOURCE_LOCATION,                                     \
                               [&](SourceLocation loc, std::string_view cond_text)                 \
                               {                                                                   \
                                   return make_exception_with_context<ScalarCompareFailed>(        \
                                       loc,                                                        \
                                       cond_text,                                                  \
                                       annotate_name_with_type(std::string{#val}, (val)),          \
                                       stringify_value(val),                                       \
                                       std::string{">="},                                          \
                                       stringify_value(ref));                                      \
                               })

#define ASSERT_LE_SCALAR(val, ref)                                                                 \
    handle_exception_condition(OpLe::apply((val), (ref)),                                          \
                               OpLe::op_text,                                                      \
                               ASSERT_CURRENT_SOURCE_LOCATION,                                     \
                               [&](SourceLocation loc, std::string_view cond_text)                 \
                               {                                                                   \
                                   return make_exception_with_context<ScalarCompareFailed>(        \
                                       loc,                                                        \
                                       cond_text,                                                  \
                                       annotate_name_with_type(std::string{#val}, (val)),          \
                                       stringify_value(val),                                       \
                                       std::string{"<="},                                          \
                                       stringify_value(ref));                                      \
                               })

#define ASSERT_GT_SCALAR(val, ref)                                                                 \
    handle_exception_condition(OpGt::apply((val), (ref)),                                          \
                               OpGt::op_text,                                                      \
                               ASSERT_CURRENT_SOURCE_LOCATION,                                     \
                               [&](SourceLocation loc, std::string_view cond_text)                 \
                               {                                                                   \
                                   return make_exception_with_context<ScalarCompareFailed>(        \
                                       loc,                                                        \
                                       cond_text,                                                  \
                                       annotate_name_with_type(std::string{#val}, (val)),          \
                                       stringify_value(val),                                       \
                                       std::string{">"},                                           \
                                       stringify_value(ref));                                      \
                               })

#define ASSERT_LT_SCALAR(val, ref)                                                                 \
    handle_exception_condition(OpLt::apply((val), (ref)),                                          \
                               OpLt::op_text,                                                      \
                               ASSERT_CURRENT_SOURCE_LOCATION,                                     \
                               [&](SourceLocation loc, std::string_view cond_text)                 \
                               {                                                                   \
                                   return make_exception_with_context<ScalarCompareFailed>(        \
                                       loc,                                                        \
                                       cond_text,                                                  \
                                       annotate_name_with_type(std::string{#val}, (val)),          \
                                       stringify_value(val),                                       \
                                       std::string{"<"},                                           \
                                       stringify_value(ref));                                      \
                               })

#define ASSERT_FINITE_SCALAR(val)                                                                  \
    ASSERT_ENSURE(is_finite_scalar((val)),                                                         \
                  ScalarNotFinite,                                                                 \
                  annotate_name_with_type(std::string{#val}, (val)),                               \
                  stringify_value(val))

// =====================================
// additional exception types (from TODO.md)
// =====================================

// 逻辑/契约
ASSERT_DECL_EXCEPTION2(InvalidArgument, "invalid argument", std::string, param, std::string, reason)
ASSERT_DECL_EXCEPTION1(IllegalState, "illegal state", std::string, state)
ASSERT_DECL_EXCEPTION1(PreconditionFailed, "precondition failed", std::string, name)
ASSERT_DECL_EXCEPTION1(PostconditionFailed, "postcondition failed", std::string, name)
ASSERT_DECL_EXCEPTION1(ContractViolation, "contract violation", std::string, detail)
ASSERT_DECL_EXCEPTION0(UnreachableCode, "unreachable code")
ASSERT_DECL_EXCEPTION1(FeatureUnavailable, "feature unavailable", std::string, feature)

// 范围/索引
ASSERT_DECL_EXCEPTION2(IndexOutOfRange, "index out of range", std::size_t, index, std::size_t, size)
ASSERT_DECL_EXCEPTION2(
    DimensionMismatch, "dimension mismatch", std::size_t, expected, std::size_t, actual)
ASSERT_DECL_EXCEPTION2(SizeMismatch, "size mismatch", std::size_t, expected, std::size_t, actual)
ASSERT_DECL_EXCEPTION1(EmptyContainerAccess, "empty container access", std::string, container)
ASSERT_DECL_EXCEPTION2(KeyNotFound, "key not found", std::string, key, std::string, container)
ASSERT_DECL_EXCEPTION2(LengthExceeded, "length exceeded", std::size_t, length, std::size_t, max)
ASSERT_DECL_EXCEPTION1(IteratorInvalid, "iterator invalid", std::string, reason)
ASSERT_DECL_EXCEPTION1(IteratorRangeInvalid, "iterator range invalid", std::string, reason)
ASSERT_DECL_EXCEPTION2(ScalarNotFinite, "scalar not finite", std::string, name, std::string, value)
ASSERT_DECL_EXCEPTION4(ScalarOutOfRange,
                       "scalar out of range",
                       std::string,
                       name,
                       std::string,
                       value,
                       std::string,
                       min,
                       std::string,
                       max)
ASSERT_DECL_EXCEPTION4(ScalarCompareFailed,
                       "scalar comparison failed",
                       std::string,
                       name,
                       std::string,
                       value,
                       std::string,
                       op,
                       std::string,
                       ref)

// 数值计算
ASSERT_DECL_EXCEPTION1(DivideByZero, "divide by zero", std::string, operand)
ASSERT_DECL_EXCEPTION1(Overflow, "overflow", std::string, operation)
ASSERT_DECL_EXCEPTION1(Underflow, "underflow", std::string, operation)
ASSERT_DECL_EXCEPTION1(NaNDetected, "nan detected", std::string, name)
ASSERT_DECL_EXCEPTION2(PrecisionLoss, "precision loss", std::string, from, std::string, to)

// 比较/类型
ASSERT_DECL_EXCEPTION2(TypeMismatch, "type mismatch", std::string, expected, std::string, actual)
ASSERT_DECL_EXCEPTION2(NotComparable, "not comparable", std::string, left, std::string, right)
ASSERT_DECL_EXCEPTION2(
    OrderViolation, "order violation", std::string, expected_order, std::string, actual_order)

// I/O 与文件系统
ASSERT_DECL_EXCEPTION2(IOError, "io error", std::string, path, std::string, reason)
ASSERT_DECL_EXCEPTION1(FileNotFound, "file not found", std::string, path)
ASSERT_DECL_EXCEPTION2(FileOpenFailed, "file open failed", std::string, path, std::string, reason)
ASSERT_DECL_EXCEPTION2(FileReadError, "file read error", std::string, path, std::string, reason)
ASSERT_DECL_EXCEPTION2(FileWriteError, "file write error", std::string, path, std::string, reason)
ASSERT_DECL_EXCEPTION1(PathNotFound, "path not found", std::string, path)
ASSERT_DECL_EXCEPTION2(PermissionDenied, "permission denied", std::string, path, std::string, op)
ASSERT_DECL_EXCEPTION2(DiskFull, "disk full", std::string, path, std::size_t, needed)

// 并发与同步
ASSERT_DECL_EXCEPTION2(ThreadError, "thread error", std::string, thread_id, std::string, reason)
ASSERT_DECL_EXCEPTION2(
    MutexLockFailed, "mutex lock failed", std::string, mutex_name, std::size_t, timeout_ms)
ASSERT_DECL_EXCEPTION2(
    DeadlockDetected, "deadlock detected", std::string, mutex_name, std::string, thread_id)
ASSERT_DECL_EXCEPTION1(RaceDetected, "race detected", std::string, resource)
ASSERT_DECL_EXCEPTION2(Timeout, "timeout", std::string, operation, std::size_t, timeout_ms)

// 资源/系统
ASSERT_DECL_EXCEPTION1(ResourceExhausted, "resource exhausted", std::string, resource)
ASSERT_DECL_EXCEPTION2(
    MemoryAllocationFailed, "memory allocation failed", std::size_t, bytes, std::string, reason)
ASSERT_DECL_EXCEPTION1(OutOfMemory, "out of memory", std::size_t, bytes)
ASSERT_DECL_EXCEPTION2(HandleInvalid, "handle invalid", std::string, handle, std::string, reason)

// 网络通信
ASSERT_DECL_EXCEPTION2(
    ConnectionFailed, "connection failed", std::string, endpoint, std::string, reason)
ASSERT_DECL_EXCEPTION2(ProtocolError, "protocol error", std::string, protocol, std::string, detail)
ASSERT_DECL_EXCEPTION2(SocketError, "socket error", std::string, operation, int, code)
ASSERT_DECL_EXCEPTION2(
    RequestTimeout, "request timeout", std::string, endpoint, std::size_t, timeout_ms)

// 配置与环境
ASSERT_DECL_EXCEPTION1(MissingEnvVar, "missing env var", std::string, name)
ASSERT_DECL_EXCEPTION2(InvalidEnvVar, "invalid env var", std::string, name, std::string, value)
ASSERT_DECL_EXCEPTION2(
    ConfigParseError, "config parse error", std::string, path, std::string, reason)
ASSERT_DECL_EXCEPTION2(
    ConfigMissingKey, "config missing key", std::string, key, std::string, section)
ASSERT_DECL_EXCEPTION2(
    ConfigValidationFailed, "config validation failed", std::string, key, std::string, reason)

// 解析/序列化
ASSERT_DECL_EXCEPTION2(ParseError, "parse error", std::string, input, std::string, reason)
ASSERT_DECL_EXCEPTION2(FormatError, "format error", std::string, format, std::string, reason)
ASSERT_DECL_EXCEPTION2(
    VersionMismatch, "version mismatch", std::string, expected, std::string, actual)
ASSERT_DECL_EXCEPTION3(ChecksumMismatch,
                       "checksum mismatch",
                       std::string,
                       expected,
                       std::string,
                       actual,
                       std::string,
                       algorithm)

// 算法/数据结构
ASSERT_DECL_EXCEPTION1(InvariantViolated, "invariant violated", std::string, name)
ASSERT_DECL_EXCEPTION1(CycleDetected, "cycle detected", std::string, entity)
ASSERT_DECL_EXCEPTION2(TopologyError, "topology error", std::string, graph, std::string, reason)
ASSERT_DECL_EXCEPTION1(NotSorted, "not sorted", std::string, container)

// Assertor 内部
ASSERT_DECL_EXCEPTION1(PolicyViolation, "policy violation", std::string, detail)
ASSERT_DECL_EXCEPTION1(LoggingError, "logging error", std::string, reason)
ASSERT_DECL_EXCEPTION1(SinkAttachFailed, "sink attach failed", std::string, reason)
ASSERT_DECL_EXCEPTION2(
    LogFileOpenFailed, "log file open failed", std::string, path, std::string, reason)
ASSERT_DECL_EXCEPTION2(LogWriteError, "log write error", std::string, path, std::string, reason)

// =====================================
// convenience macros (unified, concise)
// =====================================

/**
 * \defgroup assertor_assert_macros 断言包装宏（ASSERT_ENSURE）
 * \brief 基于 ASSERT_ENSURE 的语义化断言宏集合。
 *
 * 目的：
 * - 将常见错误场景映射为强类型异常（如 IndexOutOfRange）。
 * - 统一失败行为：由 `FailurePolicy` 决定 Throw/Abort/LogOnly。
 * - 自动携带 `SourceLocation` 和条件文本，便于定位。
 *
 * 用法示例：
 * `ASSERT_INDEX_IN(i, n);` 当 `i >= n` 时触发。
 *
 * 宏分类（非穷尽）：
 * -
 * 索引/尺寸：`ASSERT_INDEX_IN`、`ASSERT_DIM_EQ`、`ASSERT_SIZE_EQ`、`ASSERT_LEN_LE`、`ASSERT_NOT_EMPTY`、`ASSERT_HAS_KEY`
 * - 并发/时序：`ASSERT_LOCKED`、`ASSERT_TIMEOUT_OK`
 * -
 * 前后置/特性：`ASSERT_REQUIRE`、`ASSERT_POST`、`ASSERT_UNREACHABLE`、`ASSERT_FEATURE_AVAILABLE`、`ASSERT_ARG_VALID`
 * -
 * 迭代器/类型/排序：`ASSERT_ITER_VALID`、`ASSERT_ITER_RANGE_VALID`、`ASSERT_TYPE_EQ`、`ASSERT_COMPARABLE`、`ASSERT_ORDER`
 * -
 * 文件/IO/权限：`ASSERT_PATH_EXISTS`、`ASSERT_FILE_EXISTS`、`ASSERT_FILE_OPENED`、`ASSERT_FILE_READ_OK`、`ASSERT_FILE_WRITE_OK`、`ASSERT_IO_OK`、`ASSERT_PERMISSION`、`ASSERT_DISK_SPACE_OK`
 * - 线程：`ASSERT_THREAD_OK`、`ASSERT_NO_DEADLOCK`、`ASSERT_NO_RACE`
 * -
 * 内存/资源：`ASSERT_ALLOC`、`ASSERT_MEMORY_AVAILABLE`、`ASSERT_HANDLE_VALID`、`ASSERT_RESOURCE_AVAILABLE`
 * - 网络：`ASSERT_CONNECTED`、`ASSERT_PROTOCOL_OK`、`ASSERT_SOCKET_OK`、`ASSERT_REQUEST_TIMEOUT_OK`
 * -
 * 环境/配置/解析：`ASSERT_ENV_EXISTS`、`ASSERT_ENV_VALID`、`ASSERT_CONFIG_PARSED`、`ASSERT_CONFIG_HAS_KEY`、`ASSERT_CONFIG_VALID`、`ASSERT_PARSED`、`ASSERT_FORMAT_OK`、`ASSERT_VERSION_EQ`、`ASSERT_CHECKSUM_EQ`
 * - 结构/约束：`ASSERT_INVARIANT`、`ASSERT_NO_CYCLE`、`ASSERT_TOPOLOGY_OK`、`ASSERT_SORTED_OK`
 * -
 * 框架/日志：`ASSERT_POLICY_OK`、`ASSERT_LOG_SINK_ATTACHED`、`ASSERT_LOG_FILE_OPENED`、`ASSERT_LOG_WRITE_OK`
 *
 * 所有宏均以 `ASSERT_ENSURE` 为底层，构造 `AssertionFailed` 等异常类型。
 * @{
 */
#define ASSERT_INDEX_IN(idx, size)                                                                 \
    ASSERT_ENSURE(((std::size_t)(idx) < (std::size_t)(size)),                                      \
                  IndexOutOfRange,                                                                 \
                  (std::size_t)(idx),                                                              \
                  (std::size_t)(size))

#define ASSERT_DIM_EQ(a, b)                                                                        \
    ASSERT_ENSURE(((std::size_t)(a) == (std::size_t)(b)),                                          \
                  DimensionMismatch,                                                               \
                  (std::size_t)(b),                                                                \
                  (std::size_t)(a))

#define ASSERT_SIZE_EQ(a, b)                                                                       \
    ASSERT_ENSURE(                                                                                 \
        ((std::size_t)(a) == (std::size_t)(b)), SizeMismatch, (std::size_t)(b), (std::size_t)(a))

#define ASSERT_NOT_EMPTY(c)                                                                        \
    ASSERT_ENSURE(!(c).empty(), EmptyContainerAccess, annotate_name_with_type(std::string{#c}, (c)))

#define ASSERT_HAS_KEY(container, key)                                                             \
    ASSERT_ENSURE(((container).find(key) != (container).end()),                                    \
                  KeyNotFound,                                                                     \
                  stringify_value(key),                                                            \
                  annotate_name_with_type(std::string{#container}, (container)))

#define ASSERT_LEN_LE(n, max)                                                                      \
    ASSERT_ENSURE(((std::size_t)(n) <= (std::size_t)(max)),                                        \
                  LengthExceeded,                                                                  \
                  (std::size_t)(n),                                                                \
                  (std::size_t)(max))

#define ASSERT_NONZERO(x) ASSERT_ENSURE(((x) != 0), DivideByZero, std::string{#x})

#define ASSERT_LOCKED(ok, mutex_name, timeout_ms)                                                  \
    ASSERT_ENSURE((ok), MutexLockFailed, std::string{(mutex_name)}, (std::size_t)(timeout_ms))

#define ASSERT_TIMEOUT_OK(ok, op, timeout_ms)                                                      \
    ASSERT_ENSURE((ok), Timeout, std::string{(op)}, (std::size_t)(timeout_ms))

// ---- logical/contract ----
#define ASSERT_REQUIRE(cond, name) ASSERT_ENSURE((cond), PreconditionFailed, std::string{(name)})
#define ASSERT_POST(cond, name) ASSERT_ENSURE((cond), PostconditionFailed, std::string{(name)})
#define ASSERT_UNREACHABLE() ASSERT_ENSURE(false, UnreachableCode)
#define ASSERT_FEATURE_AVAILABLE(ok, feature)                                                      \
    ASSERT_ENSURE((ok), FeatureUnavailable, std::string{(feature)})
#define ASSERT_ARG_VALID(ok, param, reason)                                                        \
    ASSERT_ENSURE((ok), InvalidArgument, std::string{(param)}, std::string{(reason)})

// ---- iterators & ranges ----
#define ASSERT_ITER_VALID(ok, reason) ASSERT_ENSURE((ok), IteratorInvalid, std::string{(reason)})
#define ASSERT_ITER_RANGE_VALID(ok, reason)                                                        \
    ASSERT_ENSURE((ok), IteratorRangeInvalid, std::string{(reason)})

// ---- comparison/type ----
#define ASSERT_TYPE_EQ(ok, expected, actual)                                                       \
    ASSERT_ENSURE((ok), TypeMismatch, std::string{(expected)}, std::string{(actual)})
#define ASSERT_COMPARABLE(ok, left, right)                                                         \
    ASSERT_ENSURE((ok), NotComparable, std::string{(left)}, std::string{(right)})
#define ASSERT_ORDER(ok, expected_order, actual_order)                                             \
    ASSERT_ENSURE((ok), OrderViolation, std::string{(expected_order)}, std::string{(actual_order)})

// ---- I/O & filesystem ----
#define ASSERT_PATH_EXISTS(ok, path) ASSERT_ENSURE((ok), PathNotFound, std::string{(path)})
#define ASSERT_FILE_EXISTS(ok, path) ASSERT_ENSURE((ok), FileNotFound, std::string{(path)})
#define ASSERT_FILE_OPENED(ok, path, reason)                                                       \
    ASSERT_ENSURE((ok), FileOpenFailed, std::string{(path)}, std::string{(reason)})
#define ASSERT_FILE_READ_OK(ok, path, reason)                                                      \
    ASSERT_ENSURE((ok), FileReadError, std::string{(path)}, std::string{(reason)})
#define ASSERT_FILE_WRITE_OK(ok, path, reason)                                                     \
    ASSERT_ENSURE((ok), FileWriteError, std::string{(path)}, std::string{(reason)})
#define ASSERT_IO_OK(ok, path, reason)                                                             \
    ASSERT_ENSURE((ok), IOError, std::string{(path)}, std::string{(reason)})
#define ASSERT_PERMISSION(ok, path, op)                                                            \
    ASSERT_ENSURE((ok), PermissionDenied, std::string{(path)}, std::string{(op)})
#define ASSERT_DISK_SPACE_OK(ok, path, needed)                                                     \
    ASSERT_ENSURE((ok), DiskFull, std::string{(path)}, (std::size_t)(needed))

// ---- concurrency ----
#define ASSERT_THREAD_OK(ok, thread_id, reason)                                                    \
    ASSERT_ENSURE((ok), ThreadError, std::string{(thread_id)}, std::string{(reason)})
#define ASSERT_NO_DEADLOCK(ok, mutex_name, thread_id)                                              \
    ASSERT_ENSURE((ok), DeadlockDetected, std::string{(mutex_name)}, std::string{(thread_id)})
#define ASSERT_NO_RACE(ok, resource) ASSERT_ENSURE((ok), RaceDetected, std::string{(resource)})

// ---- resource/system ----
#define ASSERT_ALLOC(ptr, bytes, reason)                                                           \
    ASSERT_ENSURE(                                                                                 \
        ((ptr) != nullptr), MemoryAllocationFailed, (std::size_t)(bytes), std::string{(reason)})
#define ASSERT_MEMORY_AVAILABLE(ok, bytes) ASSERT_ENSURE((ok), OutOfMemory, (std::size_t)(bytes))
#define ASSERT_HANDLE_VALID(ok, handle, reason)                                                    \
    ASSERT_ENSURE((ok), HandleInvalid, std::string{(handle)}, std::string{(reason)})
#define ASSERT_RESOURCE_AVAILABLE(ok, resource)                                                    \
    ASSERT_ENSURE((ok), ResourceExhausted, std::string{(resource)})

// ---- network ----
#define ASSERT_CONNECTED(ok, endpoint, reason)                                                     \
    ASSERT_ENSURE((ok), ConnectionFailed, std::string{(endpoint)}, std::string{(reason)})
#define ASSERT_PROTOCOL_OK(ok, protocol, detail)                                                   \
    ASSERT_ENSURE((ok), ProtocolError, std::string{(protocol)}, std::string{(detail)})
#define ASSERT_SOCKET_OK(ok, operation, code)                                                      \
    ASSERT_ENSURE((ok), SocketError, std::string{(operation)}, (int)(code))
#define ASSERT_REQUEST_TIMEOUT_OK(ok, endpoint, timeout_ms)                                        \
    ASSERT_ENSURE((ok), RequestTimeout, std::string{(endpoint)}, (std::size_t)(timeout_ms))

// ---- config & env ----
#define ASSERT_ENV_EXISTS(name)                                                                    \
    ASSERT_ENSURE((std::getenv((name)) != nullptr), MissingEnvVar, std::string{(name)})
#define ASSERT_ENV_VALID(ok, name, value)                                                          \
    ASSERT_ENSURE((ok), InvalidEnvVar, std::string{(name)}, std::string{(value)})
#define ASSERT_CONFIG_PARSED(ok, path, reason)                                                     \
    ASSERT_ENSURE((ok), ConfigParseError, std::string{(path)}, std::string{(reason)})
#define ASSERT_CONFIG_HAS_KEY(ok, key, section)                                                    \
    ASSERT_ENSURE((ok), ConfigMissingKey, std::string{(key)}, std::string{(section)})
#define ASSERT_CONFIG_VALID(ok, key, reason)                                                       \
    ASSERT_ENSURE((ok), ConfigValidationFailed, std::string{(key)}, std::string{(reason)})

// ---- parse/serialization ----
#define ASSERT_PARSED(ok, input, reason)                                                           \
    ASSERT_ENSURE((ok), ParseError, std::string{(input)}, std::string{(reason)})
#define ASSERT_FORMAT_OK(ok, format, reason)                                                       \
    ASSERT_ENSURE((ok), FormatError, std::string{(format)}, std::string{(reason)})
#define ASSERT_VERSION_EQ(expected, actual)                                                        \
    ASSERT_ENSURE((std::string{(expected)} == std::string{(actual)}),                              \
                  VersionMismatch,                                                                 \
                  std::string{(expected)},                                                         \
                  std::string{(actual)})
#define ASSERT_CHECKSUM_EQ(expected, actual, algorithm)                                            \
    ASSERT_ENSURE((std::string{(expected)} == std::string{(actual)}),                              \
                  ChecksumMismatch,                                                                \
                  std::string{(expected)},                                                         \
                  std::string{(actual)},                                                           \
                  std::string{(algorithm)})

// ---- algorithms/data structures ----
#define ASSERT_INVARIANT(cond, name) ASSERT_ENSURE((cond), InvariantViolated, std::string{(name)})
#define ASSERT_NO_CYCLE(ok, entity) ASSERT_ENSURE((ok), CycleDetected, std::string{(entity)})
#define ASSERT_TOPOLOGY_OK(ok, graph, reason)                                                      \
    ASSERT_ENSURE((ok), TopologyError, std::string{(graph)}, std::string{(reason)})
#define ASSERT_SORTED_OK(ok, container) ASSERT_ENSURE((ok), NotSorted, std::string{(container)})

// ---- assertor internal ----
#define ASSERT_POLICY_OK(ok, detail) ASSERT_ENSURE((ok), PolicyViolation, std::string{(detail)})
#define ASSERT_LOG_SINK_ATTACHED(ok, reason)                                                       \
    ASSERT_ENSURE((ok), SinkAttachFailed, std::string{(reason)})
#define ASSERT_LOG_FILE_OPENED(ok, path, reason)                                                   \
    ASSERT_ENSURE((ok), LogFileOpenFailed, std::string{(path)}, std::string{(reason)})
#define ASSERT_LOG_WRITE_OK(ok, path, reason)                                                      \
    ASSERT_ENSURE((ok), LogWriteError, std::string{(path)}, std::string{(reason)})
/** @} */

} // namespace Assertor

} // namespace kp